//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.common.event;

import java.util.Date;

import com.saasovation.common.AssertionConcern;
import com.saasovation.common.domain.model.DomainEvent;

/**
 * 已存储事件
 */
public class StoredEvent extends AssertionConcern {
    /**
     * 事件主体
     */
    private String eventBody;
    /**
     * 事件编号
     */
    private long eventId;
    /**
     * 发生日期
     */
    private Date occurredOn;
    /**
     * 类型名
     */
    private String typeName;

    /**
     * 使用以下参数构造已存储事件
     * @param aTypeName     类型名
     * @param anOccurredOn  发生日期
     * @param anEventBody   事件主体
     */
    public StoredEvent(String aTypeName, Date anOccurredOn, String anEventBody) {
        this();

        this.setEventBody(anEventBody);
        this.setOccurredOn(anOccurredOn);
        this.setTypeName(aTypeName);
    }

    /**
     * 使用以下参数构造已存储事件
     * @param aTypeName     类型名
     * @param anOccurredOn  发生日期
     * @param anEventBody   事件主体
     * @param anEventId     事件编号
     */
    public StoredEvent(String aTypeName, Date anOccurredOn, String anEventBody, long anEventId) {
        this(aTypeName, anOccurredOn, anEventBody);

        this.setEventId(anEventId);
    }

    /**
     * 获取事件主体
     * @return  返回字符串
     */
    public String eventBody() {
        return this.eventBody;
    }

    /**
     * 获取事件编号
     * @return  返回长整型
     */
    public long eventId() {
        return this.eventId;
    }

    /**
     * 获取发生日期
     * @return  返回日期
     */
    public Date occurredOn() {
        return this.occurredOn;
    }

    /**
     * 转换到领域事件
     * @param <T>   继承自领域事件
     * @return      返回继承自领域事件
     */
    @SuppressWarnings("unchecked")
    public <T extends DomainEvent> T toDomainEvent() {
        Class<T> domainEventClass = null;

        try {
            domainEventClass = (Class<T>) Class.forName(this.typeName());
        } catch (Exception e) {
            throw new IllegalStateException(
                    "Class load error, because: "
                    + e.getMessage());
        }

        // 由事件主体及加载类，反序列化领域事件
        T domainEvent =
            EventSerializer
                .instance()
                .deserialize(this.eventBody(), domainEventClass);

        return domainEvent;
    }

    /**
     * 获取类型名
     * @return  返回字符串
     */
    public String typeName() {
        return this.typeName;
    }

    @Override
    public boolean equals(Object anObject) {
        boolean equalObjects = false;

        if (anObject != null && this.getClass() == anObject.getClass()) {
            StoredEvent typedObject = (StoredEvent) anObject;
            equalObjects = this.eventId() == typedObject.eventId();
        }

        return equalObjects;
    }

    @Override
    public int hashCode() {
        int hashCodeValue =
            + (1237 * 233)
            + (int) this.eventId();

        return hashCodeValue;
    }

    @Override
    public String toString() {
        return "StoredEvent [eventBody=" + eventBody + ", eventId=" + eventId + ", occurredOn=" + occurredOn + ", typeName="
                + typeName + "]";
    }

    /**
     * 默认构造器
     */
    public StoredEvent() {
        super();

        this.setEventId(-1);
    }

    /**
     * 设置事件主体
     * @param anEventBody   事件主体
     */
    protected void setEventBody(String anEventBody) {
        this.assertArgumentNotEmpty(anEventBody, "The event body is required.");
        this.assertArgumentLength(anEventBody, 1, 65000, "The event body must be 65000 characters or less.");

        this.eventBody = anEventBody;
    }

    /**
     * 设置事件编号
     * @param anEventId 事件编号
     */
    protected void setEventId(long anEventId) {
        this.eventId = anEventId;
    }

    /**
     * 设置发生日期
     * @param anOccurredOn  发生日期
     */
    protected void setOccurredOn(Date anOccurredOn) {
        this.occurredOn = anOccurredOn;
    }

    /**
     * 设置类型名
     * @param aTypeName 类型名
     */
    protected void setTypeName(String aTypeName) {
        this.assertArgumentNotEmpty(aTypeName, "The event type name is required.");
        this.assertArgumentLength(aTypeName, 1, 100, "The event type name must be 100 characters or less.");

        this.typeName = aTypeName;
    }
}
